species/zmat: import vectors as a module to break import cycle#879
species/zmat: import vectors as a module to break import cycle#879
Conversation
The cycle arc.species.vectors -> arc.species.converter -> arc.species.zmat -> arc.species.vectors has existed since 2023 (when calculate_param and get_vector_length were moved into arc.species.vectors). It only stays latent because arc/species/__init__.py loads converter before anything reaches vectors, and because vectors.py uses a module-style import for converter. Mirror that pattern in zmat.py: import vectors as a module rather than pulling specific names out via from-import. Module imports tolerate partial initialization during a cycle; from-name imports do not. This clears the CodeQL "Module-level cyclic import" warnings on zmat.py and removes a footgun for whoever next reorders the package init.
There was a problem hiding this comment.
Pull request overview
Resolves a latent module-level import cycle in arc.species by switching zmat.py from name-imports out of vectors to importing the vectors module and qualifying call sites, making imports resilient to partial initialization during cyclical imports.
Changes:
- Replace
from arc.species.vectors import ...withfrom arc.species import vectorsinzmat.py. - Prefix all
calculate_param/get_vector_lengthcall sites withvectors.to match the new import style.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #879 +/- ##
==========================================
- Coverage 60.47% 60.44% -0.03%
==========================================
Files 102 102
Lines 31099 31096 -3
Branches 8103 8103
==========================================
- Hits 18806 18796 -10
- Misses 9951 9957 +6
- Partials 2342 2343 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
The previous commit changed zmat.py to a module-style import of vectors but the structural cycle (zmat -> vectors -> converter -> zmat) remained, so CodeQL kept flagging both edges. vectors.py only used converter in get_vector, where it called xyz_to_x_y_z(xyz) (O(N) per-axis split + check_xyz_dict validation) just to read two atoms' coordinates. Inline the two coord lookups directly and drop the module-level converter import. The cycle is now fully broken at the import-graph level.
alongd
left a comment
There was a problem hiding this comment.
Excellent, thanks for getting to the bottom of this!
| dy = y[anchor] - y[pivot] | ||
| dz = z[anchor] - z[pivot] | ||
| return [dx, dy, dz] | ||
| px, py, pz = xyz['coords'][pivot] |
There was a problem hiding this comment.
And it's even more efficient now :)
Summary
arc.species.zmat→arc.species.vectors→arc.species.converter→arc.species.zmatwas a real module-level import cycle, latent since 2023. It only stayed harmless becausearc/species/__init__.pyhappened to load
converterbefore anything reachingvectors. CodeQL flagged both ends:zmat.py:39(cycle origin) and sevenconverter.py:33-39"may not be defined" errors on the from-name imports of zmatsymbols.
f07d1f21): switcheszmat.pyto module-stylefrom arc.species import vectorsand prefixes the 17 call sites. This was a necessary but insufficient step — module imports tolerate partialinitialization mid-cycle, but the structural cycle still existed.
0f5d3ad9): actually breaks the cycle by removingvectors.py's module-level import ofconverter. The only call site (get_vector) was usingconverter.xyz_to_x_y_z(xyz)to build threefull-length axis tuples just to read two atoms' coordinates — replaced with two direct
xyz['coords'][i]lookups. Net: 6 lines → 3 lines, O(N) → O(1), and the heavycheck_xyz_dictvalidation overhead is gone(caller passes a well-formed dict).
vectorsback tozmat, so all 8 CodeQL alerts clear.